// -----------------------------------------------------------------------
// <copyright file="SessionManager.cs" company="Microsoft">
// Manage with sessions
// </copyright>
// -----------------------------------------------------------------------
namespace WebServer.BusinessLogic.Web
{
    using System;
    using System.Collections.ObjectModel;
    using System.Linq;

    /// <summary>
    /// Class for managing with sessions
    /// </summary>
    public class SessionManager : ISessionManager
    {
        /// <summary>
        /// Desired name of the cookie
        /// </summary>
        private const string SessionCookieName = "sessionKey";

        /// <summary>
        /// Static field for creation Singleton
        /// </summary>
        private static SessionManager instance;

        private Configurator _configurator;

        public SessionManager(Configurator configurator = null)
        {
            if (configurator == null)
            {
                configurator = Configurator.Instance;
            }
            _configurator = configurator;

            SessionList = new Collection<Session>();
        }

        /// <summary>
        /// Gets session singleton instance
        /// </summary>
        public static SessionManager Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new SessionManager();
                }

                return instance;
            }
        }

        /// <summary>
        /// Gets or sets Count of connected to the server's session users
        /// </summary>
        public int ConnectedUsersCount { get; set; }

        /// <summary>
        /// Gets or sets Date of session creation
        /// </summary>
        public DateTime DateCreated { get; set; }

        /// <summary>
        /// Gets List of all sessions of webserver
        /// </summary>
        public Collection<Session> SessionList { get; private set; }

        /// <summary>
        /// Gets a value indicating whether	Cookieless session stores session data in URL
        /// </summary>
        public bool IsCookieless
        {
            get { return _configurator.IsCookielessSession; }
        }

        /// <summary>
        /// Getting session by key
        /// </summary>
        /// <param name="sessionKey">key of session</param>
        /// <param name="request">request object</param>
        /// <returns>Session object with session key = SessionKey</returns>
        public Session GetSessionByKey(string sessionKey, Request request)
        {
            // create validation for current user
            var currentUserValidation = Session.CreateUniqueValidation(request);

            // only compare current sessions by SessionKey and validation
            var item = this.SessionList.Where(i => i.SessionKey == sessionKey && i.UniqueValidation == currentUserValidation).SingleOrDefault();
            return item;
        }

        /// <summary>
        /// Deleting session
        /// </summary>
        /// <param name="sessionKey">key of session</param>
        /// <param name="request">request object</param>
        /// <param name="response">response object</param>
        /// <returns>result of deleting</returns>
        public bool DeleteSession(string sessionKey, Request request, Response response)
        {
            var session = this.GetSessionByKey(sessionKey, request);
            if (session != null)
            {
                // remove session from list
                this.SessionList.Remove(session);

                if (!this.IsCookieless)
                {
                    // remove cookie also
                    response.Cookies.RequestDelete(SessionCookieName);
                }

                // return success
                return true;
            }

            return false;
        }

        /// <summary>
        /// Geting current session
        /// </summary>
        /// <param name="request">request object</param>
        /// <returns>session object</returns>
        public Session GetCurrentSession(Request request)
        {
            if (this.IsCookieless && request.SessionKey != null)
            {
                return this.GetSessionByKey(request.SessionKey, request);
            }

            return this.GetSessionFromCookie(request);
        }

        /// <summary>
        /// updating session
        /// </summary>
        /// <param name="request">request object</param>
        /// <param name="s">session object</param>
        public void UpdateSession(Request request, Session s)
        {
            request.SessionKey = s.SessionKey;

            var session = this.GetSessionByKey(s.SessionKey, request);
            session.LastAccessed = DateTime.Now;
            session.IsAdmin = s.IsAdmin;
            
        }

        /// <summary>
        /// Creates session and returns Session
        /// </summary>
        /// <param name="request">request object</param>
        /// <param name="response">response object</param>
        /// <returns>Session object with session key = SessionKey</returns>
        public Session CreateSession(Request request, Response response)
        {
            // try getting Session from cookie
            Session s = this.GetSessionFromCookie(request);

            if (s != null)
            {
                s.LastAccessed = DateTime.Now;
            }
            else
            {
                // session was not found in cookie, create new one
                s = new Session(request);
            }

            // check if session was created before
            if (this.GetSessionByKey(s.SessionKey, request) == null)
            {
                // add new session
                this.SessionList.Add(s);

                //if (!this.IsCookieless)
                //{
                    // add cookie with session in it
                    var c = new Cookie(SessionCookieName, s.SessionKey);

                    // set session for whole website
                    c.Path = "/";
                    c.Expires = DateTime.Now.AddDays(10);

                    // add session request to response
                    if (response != null)
                    {
                        response.Cookies.Add(c);
                    }
                //}
                //else
                //{
                    // update request so that it contains sessionKey now
                    request.SessionKey = s.SessionKey;
                //}

                // return SessionKey
                return s;
            }

            return null;
        }

        /// <summary>
        /// Get session from cookie
        /// </summary>
        /// <param name="request">request object</param>
        /// <returns>Session object with session key = SessionKey</returns>
        private Session GetSessionFromCookie(Request request)
        {
            if (!this.IsCookieless)
            {
                if (request.Cookies == null)
                {
                    return null;
                }

                Cookie cookie = request.Cookies.GetCookie(SessionCookieName);
                if (cookie != null)
                {
                    // got session value. check if server remembers it
                    var session = this.GetSessionByKey(cookie.Value, request);
                    if (session != null)
                    {
                        return session;
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// Gets URL string if session is cookieless. Like [asddad-asdasd-dasdas]
        /// </summary>
        /// <param name="request">Request to get session from</param>
        /// <returns>Session URL</returns>
        public string GetSessionUrl(Request request)
        {
            if (this.IsCookieless)
            {
                var s = GetCurrentSession(request);
                if (s != null)
                {
                    return string.Format("[{0}]", s.SessionKey);
                }
            }

            return string.Empty;
        }
    }
}